package com.chinackts.config;

import com.chinackts.auth.model.CookieStore;
import com.chinackts.auth.model.HeaderStore;
import com.chinackts.auth.repository.redis.CookieStoreRepository;
import com.chinackts.auth.repository.redis.HeaderStoreRepository;
import com.chinackts.common.okhttp.OkHttpProxySelector;
import com.chinackts.common.okhttp.OkHttpRequest;
import com.chinackts.common.okhttp.RetryInterceptor;
import com.chinackts.common.property.ProxyApiProperties;
import com.chinackts.common.utils.RequestUtil;
import okhttp3.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author Lianhong
 * @since 2021/3/5
 */
@Configuration
@EnableConfigurationProperties({ProxyApiProperties.class})
public class OkHttpConfiguration {

    private static final HeaderStore userAgent = new HeaderStore("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134");

    @Resource
    private OkHttpProxySelector okHttpProxySelector;
    @Resource
    private HeaderStoreRepository headerStoreRepository;
    @Resource
    private CookieStoreRepository cookieStoreRepository;

    private OkHttpClient.Builder okHttpClientBuilder() {
        return new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory(), x509TrustManager())
                .hostnameVerifier((s, sslSession) -> true)
                .connectionPool(pool())             //连接池
                .connectTimeout(90L, TimeUnit.SECONDS)
                .readTimeout(90L, TimeUnit.SECONDS)
                .writeTimeout(90L, TimeUnit.SECONDS)
                .addInterceptor(new RetryInterceptor(3));
    }

    @Bean
    public OkHttpClient okHttpClient() {
        return this.okHttpClientBuilder().build();
    }

    @Bean
    public OkHttpRequest okHttpRequest(OkHttpClient okHttpClient) {
        return new OkHttpRequest(okHttpClient);
    }

    @DependsOn("okHttpProxySelector")
    @Bean
    public OkHttpClient proxyOkHttpClient() {
        return this.okHttpClientBuilder()
                .proxySelector(okHttpProxySelector)
                .addInterceptor(chain -> {
                    Request request = chain.request();
                    Request.Builder builder = request.newBuilder();
                    //Request header
                    builder.header(userAgent.getName(), userAgent.getValue());
                    String id = RequestUtil.getRequestId();
                    List<HeaderStore> headers = headerStoreRepository.getHeaders(id);
                    for (HeaderStore h : headers) {
                        builder.header(h.getName(), h.getValue());
                    }
                    return chain.proceed(builder.build());
                })
                .cookieJar(new CookieJar() {
                    @Override
                    public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
                        if (CollectionUtils.isEmpty(list)) return;
                        List<CookieStore> cookies = new ArrayList<>();
                        list.forEach(c -> {
                            CookieStore storage = new CookieStore();
                            storage.convert(c);
                            cookies.add(storage);
                        });
                        String id = RequestUtil.getRequestId();
                        cookieStoreRepository.saveCookies(id, cookies);
                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl httpUrl) {
                        String id = RequestUtil.getRequestId();
                        List<CookieStore> cookies = cookieStoreRepository.getCookies(id);
                        if (CollectionUtils.isEmpty(cookies)) return new ArrayList<>();
                        List<Cookie> result = new ArrayList<>();
                        cookies.forEach(s -> result.add(s.convert()));
                        return result;
                    }
                })
                .build();
    }

    @Bean
    public OkHttpRequest proxyOkHttpRequest(OkHttpClient proxyOkHttpClient) {
        return new OkHttpRequest(proxyOkHttpClient);
    }

    /**
     * Create a new connection pool with tuning parameters appropriate for a single-user application.
     * The tuning parameters in this pool are subject to change in future OkHttp releases. Currently
     */
    public ConnectionPool pool() {
        return new ConnectionPool(800, 5L, TimeUnit.MINUTES);
    }

    public X509TrustManager x509TrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }
        };
    }

    public SSLSocketFactory sslSocketFactory() {
        try {
            //信任任何链接
            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
            sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

}
